//
//  rec_video.c
//  myffmpegdemo1
//
//  Created by yuanxuzhen on 2021/4/6.
//测试修改

#include "rec_video.h"
static int rec_status = 0;

void set_status(int status);
AVFormatContext* open_dev();
void read_data(AVFormatContext *fmt_ctx, FILE *outfile,
               AVCodecContext * enc_ctx);
void  openEncode(int width, int height, AVCodecContext** ctx);
AVFrame *create_frame(int width, int height);
void encode(AVCodecContext *enc_ctx, AVFrame * frame, AVPacket* newpkt, FILE *outfile);


#define V_WIDTH 640
#define V_HEIGTH 480

void rec_video(){
    
    //context
    AVFormatContext* format_context = NULL;
    AVCodecContext* enc_ctx = NULL;
    
    av_log_set_level(AV_LOG_DEBUG);
    
    rec_status = 1;
    
    
    char* url = "/Users/yuanxuzhen/study/mac/myffmpegdemo1/myffmpegdemo1/output/video.yuv";
    //创建文件
    FILE *out_file = fopen(url, "wb+");
    if(!out_file){
        printf("Error, Failed to open file!\n");
        goto __ERROR;
    }
    
    //打开设备
    format_context = open_dev();
    if(!format_context){
        printf("Error, Failed to open device!\n");
        goto __ERROR;
    }
    openEncode(V_WIDTH, V_HEIGTH, &enc_ctx);
    
    //encode
    read_data(format_context, out_file, enc_ctx);
    
    
__ERROR:
    
    if(format_context){
        avformat_close_input(&format_context);
        format_context = NULL;
    }
    
    if(out_file){
        fclose(out_file);
    }
    
    av_log(NULL, AV_LOG_DEBUG, "finish!\n");
    return;
}




void set_status(int status){
    rec_status = status;
}




AVFormatContext* open_dev(){
    int ret = 0;
    char error[1024] = {0, };
    AVFormatContext* format_context = NULL ;
    char *devicename="0:";
    AVDictionary *options = NULL;
    avdevice_register_all();
    AVInputFormat * iformat = av_find_input_format("avfoundation");
    
    av_dict_set(&options, "video_size", "640x480", 0);
    av_dict_set(&options, "framerate", "30", 0);
    av_dict_set(&options, "pixel_format", "nv12", 0);

    ret = avformat_open_input(&format_context, devicename, iformat, &options);
    av_dump_format(format_context, 0, devicename, 0);
    if(ret < 0){
        av_strerror(ret, error, 1024);
        printf(stderr, "Failed to open audio devices, [%d] %s\n", ret, error);
        return NULL;
    };
    return format_context;
}



void read_data(AVFormatContext *fmt_ctx,
               FILE *outfile,
               AVCodecContext * enc_ctx){
    char* url = "/Users/yuanxuzhen/study/mac/myffmpegdemo1/myffmpegdemo1/output/video.h264";
    //创建文件
    FILE *h264_out_file = fopen(url, "wb+");
    int ret = 0;
    AVPacket pkt;
    AVFrame *frame = NULL;
    AVPacket* new_pkt = NULL;
    int base = 0;
    frame = create_frame(V_WIDTH, V_HEIGTH);
    new_pkt = av_packet_alloc();
    if(!new_pkt){
        printf("Error, new_pkt !\n");
    }
    while ((ret = av_read_frame(fmt_ctx, &pkt)) == 0 || ret == -35) {
        if(!rec_status){
            break;
        }
        if(ret == -35){
            usleep(100);
            continue;
        }
        
        
        memcpy(frame->data[0], pkt.data, 640 * 480);
        for(int i = 0; i < 640 * 480 / 4;i++){
            frame->data[1][i] = pkt.data[640 * 480 + i * 2];
            frame->data[2][i] = pkt.data[640 * 480 + i * 2 + 1];
        }
        fwrite(frame->data[0], 1, 640 * 480, outfile);
        fwrite(frame->data[1], 1, 640 * 480 / 4 , outfile);
        fwrite(frame->data[2], 1, 640 * 480 / 4, outfile);

        printf("pkt, pkt.size=%d!\n", pkt.size);
        fflush(outfile);
        frame->pts = base++;

        encode(enc_ctx, frame, new_pkt, h264_out_file);

        
        av_packet_unref(&pkt);
    }
    encode(enc_ctx, NULL, new_pkt, h264_out_file);

    if(h264_out_file){
        fclose(h264_out_file);
    }
    
}


void  openEncode(int width, int height, AVCodecContext** ctx){
    AVCodec *codec = NULL;
    int ret = 0;
//    codec = avcodec_find_encoder(AV_CODEC_ID_H264);
    codec =  avcodec_find_encoder_by_name("libx264");
    if(!codec){
        printf("codec libx 264 not find");
        exit(1);
    }
    * ctx =  avcodec_alloc_context3(codec);
    if(!(* ctx)){
        printf("avcodec_alloc_context3 fail");
        exit(1);
    }
    (*ctx)->profile = FF_PROFILE_H264_HIGH_444;
    (*ctx)->level = 50; //标识level 是5.0
    
    (*ctx)->width = width;
    (*ctx)->height = height;
    
    (*ctx)->gop_size = 250;
    (*ctx)->keyint_min = 25;
    
    (*ctx)->max_b_frames = 3;
    (*ctx)->has_b_frames = 1;
    
    (*ctx)->refs = 3;
    
    (*ctx)->pix_fmt = AV_PIX_FMT_YUV420P;
    
    (*ctx)->bit_rate = 600000;
    
    (*ctx)->time_base = (AVRational){1, 25};
    (*ctx)->framerate = (AVRational){25, 1};
    
    ret =avcodec_open2((*ctx), codec, NULL);
    if(ret < 0){
        printf("avcodec_open2  error = %s" , av_err2str(ret) );
        exit(1);
    }
    
    
    
}

AVFrame *create_frame(int width, int height){
    int ret = 0;
    AVFrame* frame = av_frame_alloc();
    if(!frame){
        printf("av_frame_alloc fail");
        goto __ERROR;
    }
    
    frame->width = width;
    frame->height = height;
    frame->format = AV_PIX_FMT_YUV420P;
    
    ret = av_frame_get_buffer(frame, 32);
    if(ret < 0){
        printf("av_frame_get_buffer fail error =  %s" , av_err2str(ret));
        goto __ERROR;
    }
    return frame;
__ERROR:
    if(frame){
        av_frame_free(&frame);
    }
    return NULL;
}


void encode(AVCodecContext *enc_ctx, AVFrame * frame, AVPacket* newpkt, FILE *outfile){
    int ret = 0;
    if(!enc_ctx){
        printf("encode  enc_ctx =  null");
        exit(1);
    }
    
    
    if(!newpkt){
        printf("encode  newpkt =  null");
        exit(1);
    }
    
    if(!outfile){
        printf("encode  newpkt =  null");
        exit(1);
    }
    
    if(frame){
           printf("send frame to encoder, pts=%lld", frame->pts);
    }
    ret = avcodec_send_frame(enc_ctx, frame);
    
    if(ret < 0){
        printf("encode  avcodec_send_frame  error");
        exit(1);
    }

    
    while (ret >= 0) {
        ret = avcodec_receive_packet(enc_ctx, newpkt);
        if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
            return;
        }else if(ret < 0 ){
            printf("encode  avcodec_receive_packet error\n");
            exit(1);
        }
        fwrite(newpkt->data, 1, newpkt->size, outfile);
        av_packet_unref(newpkt);
    
    }
}





